#pragma once
#include <string>
#include <boost/preprocessor/seq/for_each.hpp>

namespace umtl
{
	//////////////////////////////////////////////////////////////////////////
	//

	#define __make_enum_string_( elem ) #elem
	#define __declare_enum_element_( r, data, t ) t,
	#define __declare_enum_id_case_( r, data, t ) if( str == __make_enum_string_(t) ) return t;
	#define __declare_enum_string_case_( r, data, t ) table_[ t ] = __make_enum_string_(t);

	//////////////////////////////////////////////////////////////////////////
	//

	#define enum_t( enum_name, enum_args ) \
	class enum_name \
	{ \
	public: \
		enum eID { BOOST_PP_SEQ_FOR_EACH( __declare_enum_element_, ~, enum_args ) enum_end }; \
		\
		struct string_table_t \
		{ \
			char const *table_[enum_end+1]; \
			string_table_t() { \
				BOOST_PP_SEQ_FOR_EACH( __declare_enum_string_case_, ~, enum_args ); \
				table_[enum_end] = "enum_end"; \
			} \
			char const * operator[]( eID id ) { return table_[id]; } \
		}; \
		\
		enum_name( eID _id ) : id_( _id ) {} \
		enum_name( std::string const & _string ) : id_( select_id( _string ) ) {} \
		enum_name( char const * _string ) : id_( select_id( std::string( _string ? _string : "" ) ) ) {} \
		enum_name( enum_name const & other ) : id_(other.id_) {} \
		enum_name & operator=( enum_name const & other ) { id_ = other.id_; return *this; } \
		\
		eID id() { return id_; } \
		char const * string() { return string_table( id_ ); } \
		operator eID() { return id_; } \
		\
	private: \
		eID id_; \
		\
		static eID select_id( std::string const & str ) \
		{ \
			BOOST_PP_SEQ_FOR_EACH( __declare_enum_id_case_, ~, enum_args ); \
			return enum_end; \
		} \
		static char const * string_table( eID id ) { static string_table_t inst; return inst[id]; }; \
	};

	//////////////////////////////////////////////////////////////////////////
}